// Listing 19.3 :friendclass1.cpp.
// -- Type specific friend function is template
#include <iostream>
using namespace std;

const int DefaultSize = 10;

// declare a simple Animal class so that we can
// create an array of animals

class Animal
{
    public:
        Animal(int);
        Animal();
        ~Animal() {}
        int GetWeight() const { return itsWeight; }
        void Display() const { cout << itsWeight; }
    private:
        int itsWeight;    
};

Animal::Animal(int weight) :itsWeight(weight) {}

template<class T>
class Array
{
    public:
        Array(int itsSize = DefaultSize);
        Array(const Array &rhs);
        ~Array() {delete [] pType; }   

        // operators
        Array& operator=(const Array&);
        T& operator[](int offSet) { return pType[offSet]; }
        const T& operator[](int offSet) const
        {
            return pType[offSet];
        }
        // accessors
        int GetSize() const { return itsSize; }
        // friend function
        friend void Intrude(Array<int>);
    private:
        T *pType;
        int itsSize;    
};

// friend function, Not a template, can only be used
// with int arrays! Intrudes into private data.
void Intrude(Array<int> theArray)
{
    cout << endl << "*** Intrude ***" << endl;
    for (int i = 0; i < theArray.itsSize; i++)
    {
        cout << "i: " << theArray.pType[i] << endl;
    }
    cout << endl;
}

// implementations follow...
// implement the constructor
template<class T>
Array<T>::Array(int size) :itsSize(size)
{
    pType = new T[size];
    // the constructors of the type you are creating
    // should set a default 
}

// copy constructor
template<class T>
Array<T>::Array(const Array &rhs)
{
    itsSize = rhs.GetSize();
    pType = new T[itsSize];
    for (int i = 0; i < itsSize; i++)
    {
        pType[i] = rhs[i];
    }
}

// operator=
template<class T>
Array<T>& Array<T>::operator=(const Array &rhs)
{
    if (this == &rhs)
    {
        return *this;
    }

    delete [] pType;
    itsSize = rhs.GetSize();
    pType = new T[itsSize];
    for (int i = 0; i < itsSize; i++)
    {
        pType[i] = rhs[i];
    }
    return *this;
}

// driver program
int main(void)
{
    Array<int> theArray;
    Array<Animal> theZoo;
    Animal *pAnimal;

    // fill the arrays
    for (int i = 0; i < theArray.GetSize(); i++)
    {
        theArray[i] = i * 2;
        pAnimal = new Animal(i * 3);
        theZoo[i] = *pAnimal;
    }

    int j;
    for (j = 0; j < theArray.GetSize(); j++)
    {
        cout << "theZoo[" << j << "]:\t";
        theZoo[j].Display();
        cout << endl;
    }

    cout << "Now use the friend function to ";
    cout << "find the members of Array<int>";
    Intrude(theArray);

    cout << endl << "Done." << endl;
}
